import { AlicloudService } from '@app/alicloud';
import { CacheService } from '@app/cache';
import { ConfigureService } from '@app/configure';
import { CleanRecordRepositoryService } from '@app/db/clean-record-repository/clean-record-repository.service';
import { DeviceRepositoryService } from '@app/db/device-repository/device-repository.service';
import { User } from '@app/db/entity/User';
import { UserWalletLog } from '@app/db/entity/UserWalletLog';
import { WaterBills } from '@app/db/entity/WaterBills';
import { WaterTest } from '@app/db/entity/WaterTest';
import { UserRepositoryService } from '@app/db/user-repository/user-repository.service';
import { WaterTestRepositoryService } from '@app/db/water-test-repository/water-test-repository.service';
import { Injectable, OnModuleInit } from '@nestjs/common';
import { DEVICE_CTR_ORDER } from 'constant/DEVICE_CTR_ORDER';
import { DEVICE_CTR_RESULT } from 'constant/DEVICE_CTR_RESULT';
import { TEST_STATUS } from 'constant/TEST_STATUS';
import { WALLET_LOG_TYPE } from 'constant/WALLET_LOG_TYPE';
import { DataSource, FindOptionsSelect } from 'typeorm';
import { Device } from '@app/db/entity/Device';
import { WaterBillsRepositoryService } from '@app/db/water-bills-repository/water-bills-repository.service';


@Injectable()
export class MsgHandleService implements OnModuleInit{
	private aliToDeviceTopicReg:string;

	private DEVICE_MSG_OF_IOT_KEY:string
	private DEVICE_MSG_OF_REPORT_KEY:string

	constructor(
		private readonly device:DeviceRepositoryService,
		private readonly waterBills:WaterBillsRepositoryService,
		private readonly user:UserRepositoryService,
		private readonly alicloud:AlicloudService,
		private readonly config:ConfigureService,
		private dataSource: DataSource,
		private cache:CacheService,
		private waterTest:WaterTestRepositoryService,
		private cleanRecord:CleanRecordRepositoryService,

	) {
		this.aliToDeviceTopicReg = this.config.get('ALI_TO_DEVICE_TOPIC_REG')

		this.DEVICE_MSG_OF_REPORT_KEY = this.config.get('DEVICE_MSG_OF_REPORT_KEY')
		
	}


	onModuleInit() {
		// const a = {"time":1736743413,"topic":"/inakXJLjXLl/1001039/user/update","data":"{\"dCore\":\"1001039\",\"v\":\"dd_1\",\"local\":{\"mcc\":460,\"mnc\":0,\"lac\":10186,\"ci\":111319372,\"bsic\":0},\"sData\":[{\"port\":\"0\",\"val\":{\"Signal\":\"18\"}},{\"port\":\"1-1\",\"val\":{\"485_ID\":1,\"setStartStu\":0,\"eClearstu\":0,\"eFlowOnce\":599,\"setRVOf1\":78,\"setRVOf2\":78,\"setRVOf3\":85,\"setRVOf4\":78,\"setuseWtTd\":7584,\"setOvAlarmVol\":0,\"eMWaterStu\":0,\"eWtPressureSw\":0,\"eTDS_Raw\":92,\"eTDS_Clean\":6,\"OnlineStatus\":600,\"eFlow\":599,\"DevMaxWaterFlow\":150000}}]}"}

		// await this.cache.client.lpush(this.DEVICE_MSG_OF_REPORT_KEY, JSON.stringify(a))


		this.readMsg()
	}

	private async readMsg() {
		const size = 10
		while(true) {
			const msgs = await this.cache.client.rpop(this.DEVICE_MSG_OF_REPORT_KEY, size)
			if(msgs == null) {
   			}else {
				console.log('收到数据')
				const date = new Date()
				console.log(date.toLocaleDateString() + ' ' + date.toLocaleTimeString())
				console.log(msgs)
				msgs.forEach(msg => {
					if(msg) {
						
						try {
							const message = JSON.parse(msg)
							
							this.handle(message.time, message.topic, JSON.parse(message.data))
		
						} catch (error) {
							console.log(error.message)
						}
					}

				})

				if(msgs.length < size) {
					break
				}
			}
		}

		setTimeout(() => {
			this.readMsg()
		}, 5000)
	}


	private async handle(reportTime:number, topic:string, msgData:{[key:string]:any}) {
		
		const deviceId = Number(msgData.dCore)

		//if(deviceId != 1001032) return


		let updateData:{[key in keyof Device]? : Device[key]} = {
			connectStatus : 1
		}

		if(msgData.extend != undefined) {
			if(msgData.extend.SV != undefined) {
				updateData.software = msgData.extend.SV
			}
		}


		this.device.update({id : deviceId}, updateData)


		if(msgData.v == 'sd_1') { //这是服务器下发指令，然后设备响应的结果
			const key = msgData.cmd + ':' + msgData.dCore
			if(msgData.err == undefined) { //成功了
				this.cache.set(key, DEVICE_CTR_RESULT.SUCCESS, 10)

				if(msgData.cmd == DEVICE_CTR_ORDER.SET_CLEAN_DEVICE) {
					this.cleanRecord.update({id : Number(msgData.msgId)}, {status : DEVICE_CTR_RESULT.SUCCESS})
				}

			}else {
				this.cache.set(key, DEVICE_CTR_RESULT.FAIL, 10)

				if(msgData.cmd == DEVICE_CTR_ORDER.SET_CLEAN_DEVICE) {
					this.cleanRecord.update({id : Number(msgData.msgId)}, {status : DEVICE_CTR_RESULT.FAIL, err : msgData.err})
				}
			}

			return
		}

	

		if(msgData.v == 'dd_1') { //设备主动上报给平台

			let topics = topic.split("/")
			const productKey = topics[1]

			topics = null


			if(msgData.r == 1) {
				const time = Math.floor(Date.now() / 1000)
				this.pub(deviceId, productKey, {
					v : 'rs_1',
					t_req : msgData.t_req || time,
					t_resp : time
				})
			}


			if(msgData.sData && msgData.sData.length > 0) {
				msgData.sData.forEach(async item => {
					if(item.val.eLuxData != undefined || item.val.eSensorStatus != undefined) { //安检仪
						this.test(item.val, deviceId, reportTime)
						return
					}
	
					if(item.val.eFlow != undefined) {
						this.waterFlow(item.val, deviceId, productKey, reportTime).then(r => {
							
						})
					}
	
					
				})
			}
			
		}

		
	}


	

	private async test(msg, deviceId:number, reportTime:number) {
		const key = 'test.' + deviceId
		

		if(msg.eSensorStatus == undefined) { //检测过程中上报检测数据

			const id = await this.cache.get(key)
			if(id == null) {
				return
			}



			const row = await this.waterTest.findOne({id : Number(id)}, {id : true, status : true, endTime : true, type: true})

			if(!row) {
				return
			}


			if(row.status == TEST_STATUS.testing || (row.status == TEST_STATUS.end && (reportTime - row.endTime) <= 4)) {

				if(row.type == 0) {
					this.waterTest.update({id : row.id}, {
						data : () => {
							return `JSON_ARRAY_APPEND(data, '$', '{"time":${reportTime},"lux":${msg.eLuxData}}')`
						}
					})


				}else {
					this.waterTest.update({id : row.id}, {
						result : msg.eLuxData
					})
				}


				
				
			}
			

		} else {
			
			const id = await this.cache.get(key)

			console.log('id:' + id)


			if(id == null) {
				if(msg.eSensorStatus == 1) { //这是在设备上启动检测


					console.log('开启新检测1')

					this.waterTest.update({deviceId : deviceId, status : TEST_STATUS.testing}, {status : TEST_STATUS.stop})
					this.startNewTest(deviceId, msg.setCheckStep)
				}

				return
			}


			const field:FindOptionsSelect<WaterTest> = {
				id : true, status : true
			}

			// if(msg.eSensorStatus == 2) {
			// 	field.data = true
			// }



			const row = await this.waterTest.findOne({id : Number(id)}, field)


			if(msg.eSensorStatus == 0) {
				if(row.status == TEST_STATUS.start) {
					this.waterTest.update({id : row.id}, {status : TEST_STATUS.fail, endTime : reportTime})
				}else if(row.status == TEST_STATUS.testing) {
					this.waterTest.update({id : row.id}, {status : TEST_STATUS.stop, endTime : reportTime})
				}

				this.cache.del(key)
			}else if(msg.eSensorStatus == 1) {
				if(row.status == TEST_STATUS.testing) { //放弃当前的
					this.waterTest.update({id : row.id}, {status : TEST_STATUS.stop})
				}

				console.log('开启新检测2')

				this.startNewTest(deviceId, msg.setCheckStep)


				// if(row.status == TEST_STATUS.start) {
				// 	this.waterTest.update({id : row.id}, {status : TEST_STATUS.testing})
				// }
			}else if(msg.eSensorStatus == 2) {
				this.cache.client.setex(key, 3, id)
				this.waterTest.update({id : row.id}, {status : TEST_STATUS.end, endTime : reportTime})
			}
		}
		


	}

	private async startNewTest(deviceId:number, type:number) {

		const device = await this.device.findOne({id : deviceId}, {id : true, userId : true})

		const test:{[key in keyof WaterTest]? : WaterTest[key]} = {
			userId : device.userId,
			deviceId,
			createTime : Math.floor(Date.now() / 1000),
			//data : [],
			status : TEST_STATUS.testing,
			type: type
		}

		if(type == 0) {
			test.data = []
		}


		const r = await this.waterTest.insert(test)



		this.cache.set('test.' + deviceId, r.rows[0][this.waterTest.primaryKey]) //, 60*60

	}

	private async waterFlow(msg, deviceId:number, productKey:string, reportTime:number) {

		const device = await this.device.findOne({id : deviceId}, {userId : true, id : true, totalWaterUsage : true})


		

		if(!device) return

		const reply = {
			"v":"ss_1",
			"cmd":"setMaxWaterFlow",
			"goal":"1-1",
			"port":"1-1",
			"param": "0",
			"childPort":""
		}



		if(msg.eFlow > 0) {

			let amount = Number((0.01 * msg.eFlow).toFixed(2))
			let bills:KeyToValueType<WaterBills> = {
				reportTime : reportTime,
				userId : device.userId,
				deviceId : deviceId,
				amount : String(amount),
				waterUsage : msg.eFlow,
				tdsRaw : msg.eTDS_Raw,
				tdsClean : msg.eTDS_Clean,
				createTime : Math.floor(Date.now() / 1000)
			}

			if(device.userId > 0) {
				

		

				const queryRunner = await this.dataSource.createQueryRunner()
				await queryRunner.connect();
				await queryRunner.startTransaction();


				


				try {

					let user = await queryRunner.manager.findOne<User>(User, {
						where : {id : device.userId},
						select : {amount : true, rewardAmount : true},
						lock : {
							mode : 'pessimistic_read'
						}
					})


					let orderId = 0

					let rechargeBalance = Number(user.amount) //充值的余额
					let rewardBalance = Number(user.rewardAmount) //赠送的余额

					if((rechargeBalance + rewardBalance) < amount) { //余额不足
						
						await queryRunner.rollbackTransaction();
						return -1
					}



					

					let { walletLog, walletUpdate} = this.walletUpdate(amount, rechargeBalance, rewardBalance, bills, user)
					walletLog.type = WALLET_LOG_TYPE.WATER_PAY

					

					//更新钱包
					const r1 = await queryRunner.manager.update<User>(User, { id : device.userId }, walletUpdate)
					if(r1.affected < 1) {
						await queryRunner.rollbackTransaction();
						return -2
					}


					const r2 = await queryRunner.manager.insert<WaterBills>(WaterBills, bills)
					
					if(!r2.raw || r2.raw.affectedRows < 1) {
						await queryRunner.rollbackTransaction();
						return -3
					}

					walletLog.relationId = orderId = r2.raw.insertId


					//加入钱包明细
					walletLog.createTime = bills.createTime
					const r3 = await queryRunner.manager.insert<UserWalletLog>(UserWalletLog, walletLog)
					if(!r3.raw || r3.raw.affectedRows < 1) {
						await queryRunner.rollbackTransaction();
						return -4
					}

					//更新设备
					//更新钱包
					const r5 = await queryRunner.manager.update<Device>(Device, { id : deviceId }, {
						totalWaterUsage : device.totalWaterUsage + msg.eFlow
					})
					if(r5.affected < 1) {
						await queryRunner.rollbackTransaction();
						return -2
					}


					


		
					await queryRunner.commitTransaction();



					reply.param = (((user.amount + user.rewardAmount) / 0.01) * 1000).toFixed(2)


					this.pub(deviceId, productKey, reply)

					return orderId
				

					
				} catch (error) {
					console.log(error)
					await queryRunner.rollbackTransaction();
					return null
					
				} finally {
					await queryRunner.release();
				}
				
			}else {
				this.waterBills.insert(bills)
			}
			

			

		}else {
			if(device.userId > 0) {
				const user = await this.user.findOne({id : device.userId}, {amount : true, rewardAmount : true})
			
				reply.param = Math.floor(((Number(user.amount) + Number(user.rewardAmount)) / 0.01) * 1000).toString()
			}else {
				reply.param = '1000'
			}
			
		}

		this.pub(deviceId, productKey, reply)
	}


	private walletUpdate(amount:number, rechargeBalance:number, rewardBalance:number, order:KeyToValueType<WaterBills>, wallet:User) {
        let walletUpdate:{amount? : number, rewardAmount? : number} = {}
        let walletLog:{[key in keyof UserWalletLog]? : UserWalletLog[key]} = {
            userId : order.userId,
            beforeAmount : String(wallet.amount),
            beforeRewardAmount : String(wallet.rewardAmount)
        }

        if(rechargeBalance >= amount) { //充值余额够钱
            //order.feeAmount = order.chargeAmount //充电订单中充值余额支出部分
            walletUpdate.amount = (rechargeBalance - amount) //钱包充值余额，更新钱包
            

            walletLog.amount = String(-amount) //余额部分支付的
            //支出之后钱包的余额记录
            walletLog.afterAmount = String(walletUpdate.amount)
            walletLog.afterRewardAmount = String(wallet.rewardAmount) //赠送余额不变
        }else {
            let offset = amount - rechargeBalance
            
            //order.feeRewardAmount = String(offset) //充电订单中赠送余额支出部分
            walletUpdate.rewardAmount = (rewardBalance - offset) //钱包赠送余额，更新钱包
            
            walletLog.rewardAmount = String(-offset)  //赠送余额支出部分

            //支出之后钱包的赠送余额记录
            walletLog.afterRewardAmount = String(walletUpdate.rewardAmount)

            if(rechargeBalance > 0) { //如果充值余额有钱的
                //order.feeAmount = String(rechargeBalance) //充值余额全部给出
                walletUpdate.amount = 0 //钱包充值余额，更新钱包

                walletLog.amount = String(-rechargeBalance)
                walletLog.afterAmount = String(walletUpdate.amount)
            }

        }

        return {
            walletLog,
            walletUpdate
        }
    }


	private async pub(deviceId:number, productKey:string, msg:{[key:string]:any}) {
		const deviceName = String(deviceId)

		msg.dCore = deviceName
		


		


		this.alicloud.pub(productKey, this.aliToDeviceTopicReg.replace(/\?/, productKey).replace(/\?/, deviceName), deviceName, msg)
	}
	

}
